/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024.
 * Description: support print vmalloc debug info
 * Author: heyuqiang
 * Create: 2024-01-23
 */
#include <linux/mm.h>
#include <linux/rtos_sysctl.h>
#include <linux/rtos_oom_extend.h>
#include "oom_slab_vmalloc.h"

#ifdef CONFIG_RTOS_KBOX
#include <linux/set_kbox_region.h>

#define MAX_UINT (~0U)
#define MAX_LOG_SIZE (MAX_UINT/1024)

static unsigned int vmalloc_info_ksize;
static int vmallocinfo_kbox_region_id = -1;
#endif

static int vmallocinfo_debug_is_open;

void vmallocinfo_debug(unsigned long vstart)
{
#ifdef CONFIG_RTOS_KBOX
	int old_region_id = -1;
#endif
	if ((vmallocinfo_debug_is_open == 0) || (vstart != VMALLOC_START))
		return;
#ifdef CONFIG_RTOS_KBOX
	if (vmallocinfo_kbox_region_id >= 0)
		old_region_id = set_kbox_region(vmallocinfo_kbox_region_id);
#endif
	pr_warn("Show vmalloc info below:\n");
	oom_vmallocinfo_show();
#ifdef CONFIG_RTOS_KBOX
	if (old_region_id >= 0)
		restore_kbox_region(old_region_id);
#endif
}

#ifdef CONFIG_RTOS_KBOX
int proc_vmalloc_info_ksize(struct ctl_table *table, int write,
					void __user *buffer,
					size_t *lenp, loff_t *ppos)
{
	int tmpid, ret;
	unsigned int pre_size, region_size;

	if (write == 0)
		return proc_dointvec_minmax(table, write, buffer, lenp, ppos);

	pre_size = vmalloc_info_ksize;

	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
	if (ret)
		return ret;

	if (vmalloc_info_ksize == 0) {
		vmalloc_info_ksize = pre_size;
		return -EINVAL;
	}

	if (vmalloc_info_ksize > MAX_LOG_SIZE) {
		pr_err("vmalloc_info_ksize is too large!\n");
		vmalloc_info_ksize = pre_size;
		return -EINVAL;
	}
	if ((vmalloc_info_ksize % 4) != 0) {
		pr_err("Size does not align by 4 kB\n");
		vmalloc_info_ksize = pre_size;
		return -EINVAL;
	}

	region_size = vmalloc_info_ksize * 1024;
	tmpid = kern_kbox_register_region("vmalloc_info", region_size, NULL);
	if (tmpid < 0) {
		vmalloc_info_ksize = pre_size;
		pr_err("register vmalloc_info_ksize kbox region failed with %d!\n", tmpid);
		return tmpid;
	}

	vmallocinfo_kbox_region_id = tmpid;
	return 0;
}
#endif

static struct ctl_table rtos_vmalloc_debuginfo_sysctls[] = {
	{
		.procname       = "vmalloc_print_debuginfo",
		.data           = &vmallocinfo_debug_is_open,
		.maxlen         = sizeof(int),
		.mode           = 0640,
		.proc_handler   = proc_dointvec_minmax,
		.extra1         = SYSCTL_ZERO,
		.extra2         = SYSCTL_ONE,
	},
#ifdef CONFIG_RTOS_KBOX
	{
		.procname       = "vmalloc_info_kbox_ksize",
		.data           = &vmalloc_info_ksize,
		.maxlen         = sizeof(unsigned int),
		.mode           = 0640,
		.proc_handler   = proc_vmalloc_info_ksize,
	},
#endif
	{}
};

rtos_kernel_sysctls_proc_init(rtos_vmalloc_debuginfo, rtos_vmalloc_debuginfo_sysctls);
